// Copyright 2022 AndeyaLee. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package faygo

import (
	"context"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestFrameworkNew(t *testing.T) {
	app := New("testApp", "1.0")
	assert.NotNil(t, app)
	assert.Equal(t, "testApp", app.Name())
	assert.Equal(t, "1.0", app.Version())
}

func TestFrameworkNewWithDefaultVersion(t *testing.T) {
	app := New("testApp")
	assert.NotNil(t, app)
	assert.Equal(t, "testApp", app.Name())
	assert.Equal(t, "1.0", app.Version())
}

func TestContextCreation(t *testing.T) {
	req := httptest.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	ctx := NewContext(w, req)
	assert.NotNil(t, ctx)
	assert.Equal(t, "/test", ctx.Path())
	assert.Equal(t, "GET", ctx.Method())
}

func TestContextJSONResponse(t *testing.T) {
	req := httptest.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	ctx := NewContext(w, req)

	data := map[string]interface{}{
		"message": "test",
		"code":    200,
	}

	err := ctx.JSON(200, data)
	assert.NoError(t, err)
	assert.Equal(t, 200, w.Code)
	assert.Contains(t, w.Body.String(), "test")
}

func TestContextStringResponse(t *testing.T) {
	req := httptest.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	ctx := NewContext(w, req)

	err := ctx.String(200, "Hello World")
	assert.NoError(t, err)
	assert.Equal(t, 200, w.Code)
	assert.Equal(t, "Hello World", w.Body.String())
}

func TestContextCookieOperations(t *testing.T) {
	req := httptest.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	ctx := NewContext(w, req)

	// Test setting cookie
	ctx.SetCookie("test", "value", 3600)

	// Test getting cookie
	value := ctx.CookieParam("test")
	assert.Equal(t, "value", value)
}

func TestContextPathParam(t *testing.T) {
	req := httptest.NewRequest("GET", "/test/123", nil)
	w := httptest.NewRecorder()

	ctx := NewContext(w, req)

	// Simulate path parameter
	ctx.SetPathParam("id", "123")

	value := ctx.PathParam("id")
	assert.Equal(t, "123", value)
}

func TestContextQueryParam(t *testing.T) {
	req := httptest.NewRequest("GET", "/test?name=john&age=30", nil)
	w := httptest.NewRecorder()

	ctx := NewContext(w, req)

	name := ctx.QueryParam("name")
	age := ctx.QueryParam("age")

	assert.Equal(t, "john", name)
	assert.Equal(t, "30", age)
}

func TestContextWithTimeout(t *testing.T) {
	req := httptest.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	ctx := NewContext(w, req)

	// Test context with timeout
	timeoutCtx, cancel := context.WithTimeout(ctx.Request().Context(), 100*time.Millisecond)
	defer cancel()

	ctx.SetRequest(req.WithContext(timeoutCtx))

	// Test that context has timeout
	deadline, ok := ctx.Request().Context().Deadline()
	assert.True(t, ok)
	assert.True(t, time.Until(deadline) <= 100*time.Millisecond)
}

func TestHandlerStruct(t *testing.T) {
	type TestHandler struct {
		ID   int    `param:"<in:path> <required> <desc:ID> <range: 0:10>"`
		Name string `param:"<in:query> <nonzero>"`
	}

	handler := &TestHandler{}

	// Test that handler implements Handler interface
	var _ Handler = handler

	// Test parameter binding would require more complex setup
	// This is a basic structure test
	assert.NotNil(t, handler)
}

func TestMiddlewareChain(t *testing.T) {
	app := New("testApp", "1.0")

	// Test adding middleware
	middleware1 := func(ctx *Context) error {
		ctx.SetHeader("X-Middleware-1", "true")
		return nil
	}

	middleware2 := func(ctx *Context) error {
		ctx.SetHeader("X-Middleware-2", "true")
		return nil
	}

	app.Filter(middleware1, middleware2)

	// Test that middleware was added
	assert.Len(t, app.Filter(), 2)
}

func TestRouteRegistration(t *testing.T) {
	app := New("testApp", "1.0")

	handler := func(ctx *Context) error {
		return ctx.String(200, "Hello World")
	}

	// Test route registration
	app.GET("/test", handler)

	// Test that route was registered
	// This would require access to internal router state
	assert.NotNil(t, app)
}

func TestErrorHandling(t *testing.T) {
	req := httptest.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	ctx := NewContext(w, req)

	// Test error response
	err := ctx.Error(500, "Internal Server Error")
	assert.NoError(t, err)
	assert.Equal(t, 500, w.Code)
}

func TestContextHeaders(t *testing.T) {
	req := httptest.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	ctx := NewContext(w, req)

	// Test setting header
	ctx.SetHeader("Content-Type", "application/json")

	// Test getting header
	contentType := ctx.Header("Content-Type")
	assert.Equal(t, "application/json", contentType)
}

func TestContextStatus(t *testing.T) {
	req := httptest.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	ctx := NewContext(w, req)

	// Test setting status
	ctx.SetStatus(201)

	// Test getting status
	status := ctx.Status()
	assert.Equal(t, 201, status)
}
